using System.IO;
using System.Security.Cryptography;
using UnityEngine;

#region AES加密
public class AesStream : Stream
{
    private const string password = "13qUWLFI";
    private Stream baseStream;
    private AesManaged aes;
    private ICryptoTransform encryptor;
    public bool autoDisposeBaseStream { get; set; } = true;
    public AesStream(Stream baseStream, byte[] salt)
    {
        this.baseStream = baseStream;
        using (var key = new PasswordDeriveBytes(password, salt))
        {
            aes = new AesManaged();
            aes.KeySize = 128;
            aes.Mode = CipherMode.ECB;
            aes.Padding = PaddingMode.None;
            aes.Key = key.GetBytes(aes.KeySize / 8);
            aes.IV = new byte[16];
            encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
        }
    }

    private void cipher(byte[] buffer, int offset, int count, long streamPos)
    {
        var blockSizeInByte = aes.BlockSize / 8;
        var blockNumber = (streamPos / blockSizeInByte) + 1;
        var keyPos = streamPos % blockSizeInByte;

        var outBuffer = new byte[blockSizeInByte];
        var nonce = new byte[blockSizeInByte];
        var init = false;

        for (int i = offset; i < count; i++)
        {
            if (!init || (keyPos % blockSizeInByte) == 0)
            {
                System.BitConverter.GetBytes(blockNumber).CopyTo(nonce, 0);
                encryptor.TransformBlock(nonce, 0, nonce.Length, outBuffer, 0);
                if (init) keyPos = 0;
                init = true;
                blockNumber++;
            }
            buffer[i] ^= outBuffer[keyPos]; //异或生成唯一的键
            keyPos++;
        }
    }

    public override bool CanRead { get { return baseStream.CanRead; } }
    public override bool CanSeek { get { return baseStream.CanSeek; } }
    public override bool CanWrite { get { return baseStream.CanWrite; } }
    public override long Length { get { return baseStream.Length; } }
    public override long Position { get { return baseStream.Position; } set { baseStream.Position = value; } }
    public override void Flush() { baseStream.Flush(); }
    public override void SetLength(long value) { baseStream.SetLength(value); }
    public override long Seek(long offset, SeekOrigin origin) { return baseStream.Seek(offset, origin); }

    public override int Read(byte[] buffer, int offset, int count)
    {
        var streamPos = Position;
        var ret = baseStream.Read(buffer, offset, count);
        cipher(buffer, offset, count, streamPos);
        return ret;
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        cipher(buffer, offset, count, Position);
        baseStream.Write(buffer, offset, count);
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            encryptor?.Dispose();
            aes?.Dispose();
            if (autoDisposeBaseStream)
                baseStream?.Dispose();
        }

        base.Dispose(disposing);
    }
    /// <summary>
    /// 加密
    /// </summary>
    /// <param name="filePath">ab文件路径</param>
    /// <param name="abName">ab包名</param>
    /// <param name="args">需要加密的文件夹</param>
    public static void AssetBundleEncrypt(string filePath, string abName, params string[] args)
    {
        foreach (var str in args)
        {
            if (!filePath.Contains(str)) return;
        }

        if (filePath.EndsWith(".manifest")) return;//过滤

        var uniqueSalt = System.Text.Encoding.UTF8.GetBytes(abName);
        var data = File.ReadAllBytes(filePath);
        using (var baseStream = new FileStream(filePath, FileMode.OpenOrCreate))
        {
            var cryptor = new AesStream(baseStream, uniqueSalt);
            cryptor.Write(data, 0, data.Length);
        }

    }
    /// <summary>
    /// 解密
    /// </summary>
    /// <param name="filePath">ab文件路径</param>
    /// <param name="abName">ab包名</param>
    /// <param name="args">需要解密的文件夹</param>
    /// <returns></returns>
    public static AssetBundle AssetBundleDecryption(string filePath, string abName, params string[] args)
    {
        foreach (var str in args)
        {
            if (!filePath.Contains(str))
            {
                return AssetBundle.LoadFromFile(filePath);
            }
        }
        AssetBundle bundle = null;
#if UNITY_EDITOR
        FileStream fileStream;
        fileStream = new FileStream(filePath, FileMode.Open);
        var uniqueSalt = System.Text.Encoding.UTF8.GetBytes(abName);
        var uncryptor = new AesStream(fileStream, uniqueSalt);
        bundle = AssetBundle.LoadFromStream(uncryptor);
        // bundle = AssetBundle.LoadFromFile(filePath);
#else
    var www = UnityEngine.Networking.UnityWebRequest.Get(filePath);
    www.SendWebRequest();
    // 阻塞，实现同步效果
    while (!www.isDone);

    if (www.isHttpError || www.isNetworkError)
    {
        Debug.LogError(string.Format("{0}:{1}", "www.error", www.error));
    }
    else
    {
        var dataStream = new MemoryStream();
        dataStream.Write(www.downloadHandler.data, 0, www.downloadHandler.data.Length);
        var uniqueSalt = System.Text.Encoding.UTF8.GetBytes(abName);
        var uncryptor = new AesStream(dataStream, uniqueSalt);
        bundle = AssetBundle.LoadFromStream(uncryptor);
    }

    www.Dispose();
    www = null;
#endif
        return bundle;
    }
}
#endregion
